package com.demo.java.retry;

import org.springframework.retry.RecoveryCallback;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryPolicy;
import org.springframework.retry.backoff.BackOffPolicy;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;

import java.util.HashMap;
import java.util.Map;

public class RetryTest {
    public static void main(String[] args) {

        System.out.println("result: " + retryTempalteTest());
    }

    /**
     // 回退策略, 重试间隔
     NoBackOffPolicy：无退避算法策略，每次重试时立即重试
     FixedBackOffPolicy：固定时间的退避策略，需设置参数sleeper和backOffPeriod，sleeper指定等待策略，默认是Thread.sleep，即线程休眠，backOffPeriod指定休眠时间，默认1秒
     UniformRandomBackOffPolicy：随机时间退避策略，需设置sleeper、minBackOffPeriod和maxBackOffPeriod，该策略在[minBackOffPeriod,maxBackOffPeriod之间取一个随机休眠时间，minBackOffPeriod默认500毫秒，maxBackOffPeriod默认1500毫秒
     ExponentialBackOffPolicy：指数退避策略，需设置参数sleeper、initialInterval、maxInterval和multiplier，initialInterval指定初始休眠时间，默认100毫秒，maxInterval指定最大休眠时间，默认30秒，multiplier指定乘数，即下一次休眠时间为当前休眠时间*multiplier
     ExponentialRandomBackOffPolicy：随机指数退避策略，引入随机乘数可以实现随机乘数回退

     // 重试策略, 重试次数
     NeverRetryPolicy：只允许调用RetryCallback一次，不允许重试
     AlwaysRetryPolicy：允许无限重试，直到成功，此方式逻辑不当会导致死循环
     SimpleRetryPolicy：固定次数重试策略，默认重试最大次数为3次，RetryTemplate默认使用的策略
     TimeoutRetryPolicy：超时时间重试策略，默认超时时间为1秒，在指定的超时时间内允许重试
     ExceptionClassifierRetryPolicy：设置不同异常的重试策略，类似组合重试策略，区别在于这里只区分不同异常的重试
     CircuitBreakerRetryPolicy：有熔断功能的重试策略，需设置3个参数openTimeout、resetTimeout和delegate
     CompositeRetryPolicy：组合重试策略，有两种组合方式，乐观组合重试策略是指只要有一个策略允许即可以重试，
     悲观组合重试策略是指只要有一个策略不允许即可以重试，但不管哪种组合方式，组合中的每一个策略都会执行
     */
    public static boolean retryTempalteTest() {
        /** 初始化retry ******************/
        Map<Class<? extends Throwable>, Boolean> map = new HashMap<>();
//        map.put(GetConnectionTimeoutException.class, false);
        map.put(Exception.class, false);

        /** 业务开始 ****************************/
        int i = 100;
        boolean result = false;
        try {
            result = RetryBuilder.builder()
                    .retryPolicy(new SimpleRetryPolicy())
                    .backOffPolicy(new FixedBackOffPolicy())
                    .build()
                    .execute(new RetryCallback<Boolean, RuntimeException>() {
                        @Override
                        public Boolean doWithRetry(RetryContext retryContext) throws RuntimeException {
                            double num = Math.random();
                            System.out.println("business start ========== " + num);
                            if (num > 0.05) {
                                throw new RuntimeException("run error");
                            }
                            return true;
                        }
                    }, new RecoveryCallback<Boolean>() {
                        @Override
                        public Boolean recover(RetryContext retryContext) throws Exception {
                            System.out.println("more than max retry num, return false");
                            return false;
                        }
                    });
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            return false;
        }
        return result;
    }

    static class RetryBuilder {
        private RetryPolicy retryPolicy = new SimpleRetryPolicy();
        private BackOffPolicy backOffPolicy = new FixedBackOffPolicy();

        public static RetryBuilder builder() {
            return new RetryBuilder();
        }

        public RetryBuilder retryPolicy(RetryPolicy retryPolicy) {
            this.retryPolicy = retryPolicy;
            return this;
        }
        public RetryBuilder backOffPolicy(BackOffPolicy backOffPolicy) {
            this.backOffPolicy = backOffPolicy;
            return this;
        }

        public RetryTemplate build() {
            RetryTemplate retryTemplate = new RetryTemplate();
            retryTemplate.setRetryPolicy(retryPolicy);
            retryTemplate.setBackOffPolicy(backOffPolicy);
            return retryTemplate;
        }
    }
}
